﻿
/*******************************************************************************************
// ©2009 Microsoft Corporation.  This code is provided under the Microsoft Public License.
*******************************************************************************************/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media.Effects;
using System.Xml;
using System.Xml.Linq;
using ApplicationProvisioning;
using Microsoft.Rtc.Collaboration;
using Microsoft.Rtc.Collaboration.Presence;
using Microsoft.Rtc.Signaling;
using PubSubWpfClient.PubSubService;

namespace PubSubWpfClient
{
   
    /// <summary>
    /// Client for PubSub WCF Service
    /// </summary>
    public partial class Window1 : Window
    {
        private PubSubService.PubSubClient _client;

        private PubSubPlatformConfiguration _config;
        private CollaborationPlatform _collabPlatform;
        private UserEndpoint _botEndPoint;
        
        public Window1()
        {


            InitializeComponent();

            //config settings
            _config = new PubSubPlatformConfiguration();
            _config.ApplicationUserAgent = ConfigurationManager.AppSettings["applicationUserAgent"];
            _config.BotUri = ConfigurationManager.AppSettings["botUri"];
            _config.OcsServerFqdn = ConfigurationManager.AppSettings["ocsServerFqdn"];
            _config.OcsServerTlsPort = int.Parse(ConfigurationManager.AppSettings["ocsServerTlsPort"]);

            //instantiate service proxy
            _client = new PubSubClient();

            
            //databind Drop Down Boxes
            Dictionary<string,int> presence=new Dictionary<string,int>();
            presence.Add("On Hook", 3500); //This will set user's presence as available
            presence.Add("Off Hook", 6500); //This will set user's presence as busy
                
            cmbPresence2.ItemsSource = presence;
            
            txtGPSStatement2.Text = "$GPGGA,162254.00,4736.830094,N,12211.774292,W,1,03,2.36,525.6,M,-25.6,M,,*65";

            //read config users into Targetuser collection
            string configXMLDoc = "";
            using (TextReader reader = new StreamReader(System.IO.Path.Combine(Environment.CurrentDirectory, @"..\..\Config.xml")))
            {
                configXMLDoc = reader.ReadToEnd();
            }

            //Parse the config xml doc into an Xdocument
            XDocument configDoc;
            configDoc = XDocument.Parse(configXMLDoc);
            
            _config.TargetUsers =  (from User in configDoc.Element("users").Descendants("targetUser")
                                        select new TargetUserConfiguration
                                        {
                                            Uri = User.Element("uri").Value,
                                            
                                        }).ToList();

            //setup collaboration platform and endpoint
            //subscribe to user presence
            PreparePlatformAndEndpoint();
            
        }

        /// <summary>
        /// Perform cleanup when service will shutdown
        /// </summary>
        private void Cleanup(object sender,CancelEventArgs e)
        {

                //Terminate Endpoint
                _botEndPoint.EndTerminate(_botEndPoint.BeginTerminate(null, null));

                //Shutdown the CollaborationPlatform
                _collabPlatform.EndShutdown(_collabPlatform.BeginShutdown(null, null));

        }

        private void btnPublishPresence2_Click(object sender, RoutedEventArgs e)
        {
            ((DropShadowEffect)txtSipAddress2.Effect).Opacity = 0;
            ((DropShadowEffect)cmbPresence2.Effect).Opacity = 0;

            bool valid = true;

            //validation
            if (txtSipAddress2.Text.Trim().Length <= 0)
            {
                lblOp2.Content = "SIP URI is not provided.";
                ((DropShadowEffect)txtSipAddress2.Effect).Opacity = 1;
                valid = false;
            }

            if (cmbPresence2.SelectedValue == null || (int)cmbPresence2.SelectedValue <= 0)
            {
                lblOp2.Content = "Presence value is not selected.";
                ((DropShadowEffect)cmbPresence2.Effect).Opacity = 1;
                valid = false;
            }

            if (valid)
            {
                try
                {
                    //call the service method with custom message
                    bool succeeded =
                        _client.UpdatePhoneStatus((CellPhoneStatus)cmbPresence2.SelectedValue, txtSipAddress2.Text.Trim(), txtCustomMessage.Text.Trim());

                    if (succeeded)
                    {
                        lblOp2.Content = "Presence successfully published.";
                    }
                }
                catch (Exception ex)
                {
                    lblOp2.Content = "Exception: " + ex.Message;
                }
            }
        }
                

        private void btnPublishPresence4_Click(object sender, RoutedEventArgs e)
        {
            ((DropShadowEffect)txtSipAddress4.Effect).Opacity = 0;
            ((DropShadowEffect)txtGPSStatement2.Effect).Opacity = 0;

            bool valid = true;

            //validation
            if (txtGPSStatement2.Text.Trim().Length <= 0)
            {
                lblOp2.Content = "GPS Statement in NMEA approved format is required.";
                ((DropShadowEffect)txtGPSStatement2.Effect).Opacity = 1;
                valid = false;
            }
            if (txtSipAddress4.Text.Trim().Length <= 0)
            {
                lblOp2.Content = "Sip Address is not provided.";
                ((DropShadowEffect)txtSipAddress4.Effect).Opacity = 1;
                valid = false;
            }

            if (valid)
            {
                try
                {
                    //call the service method to publish user's GPS location as an activity message
                    bool succeeded =
                        _client.PublishGPSLocation(txtSipAddress4.Text.Trim(), txtGPSStatement2.Text.Trim());

                    if (succeeded)
                    {
                        lblOp2.Content = "Presence successfully published.";
                    }
                }
                catch (Exception ex)
                {
                    lblOp2.Content = "Exception: " + ex.Message;
                }
            }
        }

        /// <summary>
        /// Method to to find a certificate in the local machine’s store where the Subject Name matches the fully qualified domain name (FQDN) of the local machine.
        /// We will use this certificate to establish a TLS session with the OCS server.
        /// </summary>
        /// <returns>The certificate instance.</returns>
        private X509Certificate2 GetLocalCertificate()
        {
            //Get a handle to the local machine's certificate store
            X509Store store = new X509Store(StoreLocation.LocalMachine);
            //Open the store
            store.Open(OpenFlags.ReadOnly);
            //Get a handle to a collection of the certificates installed on the machine
            X509Certificate2Collection certificates = store.Certificates;
            //Loop through the certificates looking for one 
            //where the SubjectName matches hte FQDN of the
            //Local Machine
            foreach (X509Certificate2 certificate in certificates)
            {
                if (certificate.SubjectName.Name.Contains
                    (Dns.GetHostEntry("localhost").HostName)
                    && certificate.HasPrivateKey)
                {
                    //Return the certificate that matches
                    return certificate;
                }
            }
            //If not certs match, return null
            return null;
        }

        /// <summary>
        /// Retrieves Collaboration platform settings using App Provisioning tool
        /// </summary>
        /// <returns></returns>
        private ServerPlatformSettings GetPlatformSettings()
        {
            string appUserAgent = _config.ApplicationUserAgent;

            // Use the ApplicationProvisioning classes to get the platform settings.
            TrustedApplicationPool trustedAppPool = TrustedApplicationPool.GetApplicationPoolForLocalMachine(appUserAgent);

            if (trustedAppPool == null)
                throw new ArgumentNullException("GetApplicationPoolForLocalMachine()", "Application Pool not found");

            TrustedService trustedService = trustedAppPool.GetTrustedServiceForLocalMachine();

            // Make sure a trusted server entry exists
            if (trustedService == null)
            {
                throw new ArgumentNullException("GetTrustedServiceForLocalMachine()", "Trusted Service Entry not found");
            }

            // Make sure atleast one contact for the trusted server entry exists
            if (trustedAppPool.Contacts.Count == 0)
            {
                throw new ArgumentNullException("Contact Object not found for trusted service");
            }

            return
                new ServerPlatformSettings(
                    appUserAgent,
                    trustedService.Fqdn,
                    trustedService.Port,
                    trustedService.Gruu,
                    GetLocalCertificate()
                );
        }

        /// <summary>
        /// Setup Collaboration platform and App End point to communicate with OCS server
        /// </summary>
        private void PreparePlatformAndEndpoint()
        {
            try
            {

                //Collaboration platform initialization
                ServerPlatformSettings platformSettings = GetPlatformSettings();

                _collabPlatform = new CollaborationPlatform(platformSettings);

                //synchronously call the Startup method
                _collabPlatform.EndStartup(_collabPlatform.BeginStartup(null, null));

                //Create User Endpoint based on sip address provided
                UserEndpointSettings settings = new UserEndpointSettings(_config.BotUri,
                                                                        _config.OcsServerFqdn,
                                                                        _config.OcsServerTlsPort);
                _botEndPoint = new UserEndpoint(_collabPlatform, settings);

                //synchronously call the establish method
                _botEndPoint.EndEstablish(_botEndPoint.BeginEstablish(null, null));

                //Subscribe to the Presence of all the users
                SubscribeToPresence();
                
            }
            catch (ConnectionFailureException connFailEx)
            {
                // ConnectionFailureException will be thrown when the endpoint cannot connect to the server, or the credentials are invalid.
                
                lblOp2.Content = connFailEx.Message;                                         
            }
            catch (InvalidOperationException iOpEx)
            {
                // InvalidOperationException will be thrown when the endpoint is not in a valid state to connect. To connect, the platform must be started and the Endpoint Idle.

                lblOp2.Content = iOpEx.Message;
            }
            catch (RegisterException regEx)
            {
                // RegisterException will be thrown when the endpoint cannot be registered (usually due to bad credentials).
                lblOp2.Content = regEx.Message;
                                                            
            }
        }

        

        /// <summary>
        /// This method subscribe to the presence of all the users
        /// </summary>
        private void SubscribeToPresence()
        {
            //Subscribe for gpsLocation Presence Category
            _botEndPoint.RemotePresence.PresenceSubscriptionCategories = new string[] { "gpsLocation" };
            
            // Hook event for presence notifications prior to adding the targets
            _botEndPoint.RemotePresence.PresenceNotificationReceived +=
              new EventHandler<RemotePresenceNotificationEventArgs>
                (handlerPresenceNotificationReceived);

            // Prepare list of contacts for presence subscription
            List<RemotePresentitySubscriptionTarget> subscriptionTargets =
            new List<RemotePresentitySubscriptionTarget>();


            foreach (TargetUserConfiguration user in _config.TargetUsers)
            {
                //Add the same user
                subscriptionTargets.Add(new RemotePresentitySubscriptionTarget(user.Uri, null));

            }

            //Add the list of contacts to subscribe
            //Immediately returns first event w/current presence
            _botEndPoint.RemotePresence.EndAddTargets(_botEndPoint.RemotePresence.BeginAddTargets(subscriptionTargets, null, null));
            
        }

        private void handlerPresenceNotificationReceived(object sender, RemotePresenceNotificationEventArgs e)
        {
            string uri;

            //Multiple presence notifications may be received in one event
            foreach (RemotePresentityNotificationData notification in e.Notifications)
            {
                //parse out xml
                uri = notification.Uri;    

                //put it in textbox
                foreach (PresenceCategoryWithMetaData pcm in notification.Categories)
                {
                        //get the gpsLocation
                        string rawXml = pcm.CreateInnerDataXml();
                        if (rawXml == null || rawXml.Trim().Length == 0)
                        {
                            break;
                        }

                        StringReader reader = new StringReader(rawXml);
                        XmlDocument metadataDocument = new XmlDocument();
                        metadataDocument.Load(reader);

                        XmlNodeList nodeList = metadataDocument.ChildNodes;
                        if (nodeList == null || nodeList.Count == 0)
                        {
                            continue;
                        }

                        XmlNode node = nodeList[0];
                        if (node.Name != "publishRawCategories")
                        {
                            continue;
                        }

                        if (node.ChildNodes.Count == 0)
                            continue;
                        XmlNode gpsNode = node.ChildNodes[0].ChildNodes[0];

                        if (gpsNode == null)
                            continue;
                        string location = gpsNode.InnerText;                     

                        //Add GPS Location in list box with user uri    
                        lbCustomPresence.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
                                                            new Action(
                                                                delegate()
                                                                {
                                                                    lbCustomPresence.Items.Add(uri + ": " + location);
                                                                }
                                                            ));

                }
                
            }


        }
    }
}
